{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pL--_KGdYoBz"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "uBDvXpYzYnGj"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HQzaEQuJiW_d"
      },
      "source": [
        "# TFRecord и tf.Example\n",
        "\n",
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/load_data/tfrecord\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eСмотрите на TensorFlow.org\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/tutorials/load_data/tfrecord.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eЗапустите в Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/tutorials/load_data/tfrecord.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eИзучайте код на GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/ru/tutorials/load_data/tfrecord.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/download_logo_32px.png\" /\u003eСкачайте ноутбук\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gVN4BFQ5Dg4v",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3pkUd_9IZCFO"
      },
      "source": [
        "Чтобы эффективно читать данные будет полезно сериализовать ваши данные и держать их в наборе файлов (по 100-200MB каждый) каждый из которых может быть прочитан построчно. Это особенно верно если данные передаются по сети. Также это может быть полезно для кеширования и предобработки данных.\n",
        "\n",
        "Формат TFRecord это простой формат для хранения последовательности двоичных записей.\n",
        "\n",
        "[Protocol buffers](https://developers.google.com/protocol-buffers/) это кросс-платформенная, кросс-языковая библиотека для эффективной сериализации структурированных данных.\n",
        "\n",
        "Сообщения протокола обычно определяются файлами `.proto`. Это часто простейший способ понять тип сообщения.\n",
        "\n",
        "Сообщение `tf.Example` (или protobuf) гибкий тип сообщений, который преедставляет сопоставление `{\"string\": value}`. Он разработан для использования с TensorFlow и используется в высокоуровневых APIs таких как [TFX](https://www.tensorflow.org/tfx/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Ac83J0QxjhFt"
      },
      "source": [
        "\n",
        "Этот урок покажет как создавать, парсить и использовать сообщение `tf.Example`, а затем сериализовать читать и писать сообщения `tf.Example` в/из файлов `.tfrecord`.\n",
        "\n",
        "Замечание: Хотя эти структуры полезны, они необязательны. Нет необходимости конвертировать существующий код для использования TFRecords если вы не используете [`tf.data`](https://www.tensorflow.org/guide/datasets) и чтение данных все еще узкое место обучения. См. [Производительность конвейера входных данных](https://www.tensorflow.org/guide/performance/datasets) для советов по производительности датасета."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WkRreBf1eDVc"
      },
      "source": [
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Ja7sezsmnXph"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "try:\n",
        "  # %tensorflow_version существует только в Colab.\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf\n",
        "\n",
        "import numpy as np\n",
        "import IPython.display as display"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "e5Kq88ccUWQV"
      },
      "source": [
        "## `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VrdQHgvNijTi"
      },
      "source": [
        "### Типы данных для `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lZw57Qrn4CTE"
      },
      "source": [
        "Фундаментально `tf.Example` это соответствие `{\"string\": tf.train.Feature}`.\n",
        "\n",
        "Вид сообщений `tf.train.Feature` допускает один из следующих трех типов (См. [файл `.proto`](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto) для справки). Большинство других общих типов может быть сведено к одному из этих трех:\n",
        "\n",
        "1. `tf.train.BytesList` (можно привести следующие типы)\n",
        "\n",
        "  - `string`\n",
        "  - `byte`\n",
        "\n",
        "1. `tf.train.FloatList` (можно привести следующие типы)\n",
        "\n",
        "  - `float` (`float32`)\n",
        "  - `double` (`float64`)\n",
        "\n",
        "1. `tf.train.Int64List` (можно привести следующие типы)\n",
        "\n",
        "  - `bool`\n",
        "  - `enum`\n",
        "  - `int32`\n",
        "  - `uint32`\n",
        "  - `int64`\n",
        "  - `uint64`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_e3g9ExathXP"
      },
      "source": [
        "Чтобы преобразовать стандартный тип TensorFlow в `tf.Example`-совместимый` tf.train.Feature`, вы можете использовать приведенные ниже функции. Обратите внимание, что каждая функция принимает на вход скалярное значение и возвращает `tf.train.Feature` содержащий один из трех вышеприведенных `list` типов:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "mbsPOUpVtYxA"
      },
      "outputs": [],
      "source": [
        "# Следующая функция может быть использована чтобы преобразовать значение в тип совместимый с\n",
        "# с tf.Example.\n",
        "\n",
        "def _bytes_feature(value):\n",
        "  \"\"\"Преобразует string / byte в bytes_list.\"\"\"\n",
        "  if isinstance(value, type(tf.constant(0))):\n",
        "    value = value.numpy() # BytesList не будет распаковывать строку из EagerTensor.\n",
        "  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n",
        "\n",
        "def _float_feature(value):\n",
        "  \"\"\"Преобразует float / double в float_list.\"\"\"\n",
        "  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n",
        "\n",
        "def _int64_feature(value):\n",
        "  \"\"\"Преобразует bool / enum / int / uint в int64_list.\"\"\"\n",
        "  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Wst0v9O8hgzy"
      },
      "source": [
        "Замечание: Для простоты этот пример использует только скалярные входные данные. Простейший способ обработки нескалярных признаков - использование `tf.serialize_tensor` для конвертации тензоров в двоичнеые строки. Стоки являются скалярами в тензорфлоу. Используйте `tf.parse_tensor` для обратной конвертации двоичных сток в тензор."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vsMbkkC8xxtB"
      },
      "source": [
        "Ниже приведены несколько примеров того как работают эти функции. Обратите внимание на различные типы ввода и стандартизированные типы вывода. Если входной тип функции не совпадает с одним из приводимых типов указанных выше, функция вызовет исключение (например `_int64_feature(1.0)` выдаст ошибку поскольку `1.0` это значение с плавающей точкой и должно быть использовано с функцией `_float_feature`):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "hZzyLGr0u73y"
      },
      "outputs": [],
      "source": [
        "print(_bytes_feature(b'test_string'))\n",
        "print(_bytes_feature(u'test_bytes'.encode('utf-8')))\n",
        "\n",
        "print(_float_feature(np.exp(1)))\n",
        "\n",
        "print(_int64_feature(True))\n",
        "print(_int64_feature(1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "nj1qpfQU5qmi"
      },
      "source": [
        "Все proto сообщения могут быть сериализованы в двоичную строку с использованием метода `.SerializeToString`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "5afZkORT5pjm"
      },
      "outputs": [],
      "source": [
        "feature = _float_feature(np.exp(1))\n",
        "\n",
        "feature.SerializeToString()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "laKnw9F3hL-W"
      },
      "source": [
        "### Создание сообщения `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "b_MEnhxchQPC"
      },
      "source": [
        "Допустим вы хотите создать сообщение `tf.Example` из существующих данных. На практике данные могут прийти откуда угодно, но процедура создания сообщения `tf.Example` из одного наблюдения будет той же:\n",
        "\n",
        "1. В рамках каждого наблюдения каждое значение должно быть преобразовано в `tf.train.Feature` содержащее одно из 3 совместимых типов, с использованием одной из вышеприведенных функций.\n",
        "\n",
        "2. Вы создаете отображение (словарь) из строки названий признаков в закодированное значение признака выполненное на шаге #1.\n",
        "\n",
        "3. Отображение (map) созданное на шаге 2 конвертируется в [`Features` message](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto#L85)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "4EgFQ2uHtchc"
      },
      "source": [
        "В этом уроке вы создадите датасет с использованием NumPy.\n",
        "\n",
        "У этого датасета будет 4 признака:\n",
        "* булев признак, `False` или `True` с равной вероятностью\n",
        "* целочисленный признак - равномерно случайно выбранный из `[0, 5]`\n",
        "* строковый признак сгенерированный из табицы строк с использованием целочисленного признака в качестве индекса\n",
        "* признак с плавающей точкой из стандартного нормального распределения\n",
        "\n",
        "Рассмотрим выборку состающую из 10 000 независимых, одинаково распределенных наблюдений из каждого вышеприведенного распределения:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "CnrguFAy3YQv"
      },
      "outputs": [],
      "source": [
        "# Число наблюдений в датасете.\n",
        "n_observations = int(1e4)\n",
        "\n",
        "# Булев признак, принимающий значения False или True.\n",
        "feature0 = np.random.choice([False, True], n_observations)\n",
        "\n",
        "# Целочисленный признак, случайное число от 0 до 4.\n",
        "feature1 = np.random.randint(0, 5, n_observations)\n",
        "\n",
        "# Строковый признак\n",
        "strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])\n",
        "feature2 = strings[feature1]\n",
        "\n",
        "# Признак с плавающей точкой, из стандартного нормального распределения\n",
        "feature3 = np.random.randn(n_observations)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aGrscehJr7Jd"
      },
      "source": [
        "Каждый из этих признаков может быть приведен к `tf.Example`-совместимому типу с использованием одного из `_bytes_feature`, `_float_feature`, `_int64_feature`. Вы можете затем создать `tf.Example`-сообщение из этих закодированных признаков:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "RTCS49Ij_kUw"
      },
      "outputs": [],
      "source": [
        "def serialize_example(feature0, feature1, feature2, feature3):\n",
        "  \"\"\"\n",
        "  Создает tf.Example-сообщение готовое к записи в файл.\n",
        "  \"\"\"\n",
        "  # Создает словарь отображение имен признаков в tf.Example-совместимые\n",
        "  # типы данных.\n",
        "  feature = {\n",
        "      'feature0': _int64_feature(feature0),\n",
        "      'feature1': _int64_feature(feature1),\n",
        "      'feature2': _bytes_feature(feature2),\n",
        "      'feature3': _float_feature(feature3),\n",
        "  }\n",
        "\n",
        "  # Создает Features message с использованием tf.train.Example.\n",
        "\n",
        "  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "  return example_proto.SerializeToString()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "XftzX9CN_uGT"
      },
      "source": [
        "Возьмем, например, одно наблюдение из датасета, `[False, 4, bytes('goat'), 0.9876]`. Вы можете создать и распечатать `tf.Example`-сообщение для этого наблюдения с использованием `create_message()`. Каждое наблюдение может быть записано в виде `Features`-сообщения как указано выше. Note that the `tf.Example`-[сообщение](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto#L88) это всего лишь обертка вокруг `Features`-сообщения:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "N8BtSx2RjYcb"
      },
      "outputs": [],
      "source": [
        "# Это пример наблюдения из набора данных.\n",
        "\n",
        "example_observation = []\n",
        "\n",
        "serialized_example = serialize_example(False, 4, b'goat', 0.9876)\n",
        "serialized_example"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_pbGATlG6u-4"
      },
      "source": [
        "Для декодирования сообщения используйте метод `tf.train.Example.FromString`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "dGim-mEm6vit"
      },
      "outputs": [],
      "source": [
        "example_proto = tf.train.Example.FromString(serialized_example)\n",
        "example_proto"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o6qxofy89obI"
      },
      "source": [
        "## Детали формата TFRecords\n",
        "\n",
        "Файл TFRecord содержит последовательность записей. Файл может быть прочитан только линейно.\n",
        "\n",
        "Каждая запись содержит строку байтов для данных плюс длину данных и CRC32C (32-bit CRC использующий полином Кастаньоли) хеши для проверки целостности.\n",
        "\n",
        "Каждая запись хранится в следующих форматах:\n",
        "\n",
        "    uint64 length\n",
        "    uint32 masked_crc32_of_length\n",
        "    byte   data[length]\n",
        "    uint32 masked_crc32_of_data\n",
        "\n",
        "Записи сцеплены друг с другом и организуют файл.. CRCs\n",
        "[описаны тут](https://en.wikipedia.org/wiki/Cyclic_redundancy_check), и\n",
        "маска CRC выглядит так:\n",
        "\n",
        "    masked_crc = ((crc \u003e\u003e 15) | (crc \u003c\u003c 17)) + 0xa282ead8ul\n",
        "\n",
        "Замечание: Не обязательно использовать `tf.Example` в файлах TFRecord. `tf.Example` это всего лишь метод сериализации словарей в байтовые строки. Строки текста, закодированные данные изображений, или сериализованные тензоры (с использованием `tf.io.serialize_tensor`, и\n",
        "`tf.io.parse_tensor` при загрузке). См. модуль `tf.io` для дополнительных возможностей."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "y-Hjmee-fbLH"
      },
      "source": [
        "## Файлы TFRecord с использованием `tf.data`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GmehkCCT81Ez"
      },
      "source": [
        "Модуль `tf.data` также предоставляет инструменты для чтения и записи данных в TensorFlow."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1FISEuz8ubu3"
      },
      "source": [
        "### Запись файла TFRecord\n",
        "\n",
        "Простейший способ помещения данных в датасет это использование метода `from_tensor_slices`.\n",
        "\n",
        "Примененный к массиву он возвращает датасет скаляров:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "mXeaukvwu5_-"
      },
      "outputs": [],
      "source": [
        "tf.data.Dataset.from_tensor_slices(feature1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f-q0VKyZvcad"
      },
      "source": [
        "Примененный к кортежу массивов он возвращает датасет кортежей:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "H5sWyu1kxnvg"
      },
      "outputs": [],
      "source": [
        "features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))\n",
        "features_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "m1C-t71Nywze"
      },
      "outputs": [],
      "source": [
        "# Используйте `take(1)` чтобы взять только один пример из датасета.\n",
        "for f0,f1,f2,f3 in features_dataset.take(1):\n",
        "  print(f0)\n",
        "  print(f1)\n",
        "  print(f2)\n",
        "  print(f3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mhIe63awyZYd"
      },
      "source": [
        "Используйте метод `tf.data.Dataset.map` чтобы применить функцию к каждому элементу `Dataset`.\n",
        "\n",
        "«Функция отображения должна работать в графовом режиме TensorFlow - она должна принимать и возвращать` tf.Tensors`. Не тензорная функция, такая как `create_example`, может быть заключена в` tf.py_function`, для совместимости.\n",
        "\n",
        "Использование `tf.py_function` требует указания размерности и информации о типе, которая в противном случае недоступна:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "apB5KYrJzjPI"
      },
      "outputs": [],
      "source": [
        "def tf_serialize_example(f0,f1,f2,f3):\n",
        "  tf_string = tf.py_function(\n",
        "    serialize_example,\n",
        "    (f0,f1,f2,f3),  # передайте эти аргументы в верхнюю функцию.\n",
        "    tf.string)      # возвращаемый тип `tf.string`.\n",
        "  return tf.reshape(tf_string, ()) # Результатом является скаляр"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "lHFjW4u4Npz9"
      },
      "outputs": [],
      "source": [
        "tf_serialize_example(f0,f1,f2,f3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CrFZ9avE3HUF"
      },
      "source": [
        "Примените эту функцию к каждому элементу датасета:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "VDeqYVbW3ww9"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset = features_dataset.map(tf_serialize_example)\n",
        "serialized_features_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "DlDfuh46bRf6"
      },
      "outputs": [],
      "source": [
        "def generator():\n",
        "  for features in features_dataset:\n",
        "    yield serialize_example(*features)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "iv9oXKrcbhvX"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset = tf.data.Dataset.from_generator(\n",
        "    generator, output_types=tf.string, output_shapes=())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Dqz8C4D5cIj9"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "p6lw5VYpjZZC"
      },
      "source": [
        "И запишите их в файл TFRecord:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "vP1VgTO44UIE"
      },
      "outputs": [],
      "source": [
        "filename = 'test.tfrecord'\n",
        "writer = tf.data.experimental.TFRecordWriter(filename)\n",
        "writer.write(serialized_features_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6aV0GQhV8tmp"
      },
      "source": [
        "### Чтение TFRecord файла"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o3J5D4gcSy8N"
      },
      "source": [
        "Вы можете также прочитать TFRecord файл используя класс `tf.data.TFRecordDataset`.\n",
        "\n",
        "Больше информации об использовании TFRecord файлов с использованием `tf.data` может быть найдено [тут](https://www.tensorflow.org/guide/datasets#consuming_tfrecord_data)..\n",
        "\n",
        "Использование `TFRecordDataset`-ов может быть полезно для стандартизации входных данных и оптимизации производительности."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "6OjX6UZl-bHC"
      },
      "outputs": [],
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6_EQ9i2E_-Fz"
      },
      "source": [
        "На этом этапе датасет содержит сериализованные сообщения `tf.train.Example`. При их итерации возвращаются скалярные строки тензоров.\n",
        "\n",
        "Используйте метод `.take` чтобы показать только первые 10 записей.\n",
        "\n",
        "Замечание: итерация по `tf.data.Dataset` работает только при включенном eager execution."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "hxVXpLz_AJlm"
      },
      "outputs": [],
      "source": [
        "for raw_record in raw_dataset.take(10):\n",
        "  print(repr(raw_record))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "W-6oNzM4luFQ"
      },
      "source": [
        "Эти тензоры может распарсить используя нижеприведенную функцию. Заметьте что `feature_description`  обязателен тут поскольку датасеты используют графовое исполнение и нуждаются в этом описании для построения своей размерностной и типовой сигнатуры:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "zQjbIR1nleiy"
      },
      "outputs": [],
      "source": [
        "# Создайте описание этих признаков\n",
        "feature_description = {\n",
        "    'feature0': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature1': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature2': tf.io.FixedLenFeature([], tf.string, default_value=''),\n",
        "    'feature3': tf.io.FixedLenFeature([], tf.float32, default_value=0.0),\n",
        "}\n",
        "\n",
        "def _parse_function(example_proto):\n",
        "  # Разберите `tf.Example` proto используя вышеприведенный словарь.\n",
        "  return tf.io.parse_single_example(example_proto, feature_description)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gWETjUqhEQZf"
      },
      "source": [
        "Альтернативно, используйте `tf.parse example` чтобы распарсить весь пакет за раз. Примените эту функцию к кажому элементу датасета используя метод `tf.data.Dataset.map`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "6Ob7D-zmBm1w"
      },
      "outputs": [],
      "source": [
        "parsed_dataset = raw_dataset.map(_parse_function)\n",
        "parsed_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "sNV-XclGnOvn"
      },
      "source": [
        "Используйте eager execution чтобы показывать наблюдения в датасете. В этом наборе данных 10,000 наблюдений, но вы выведете только первые 10. Данные показываются как словарь признаков. Каждое наблюдение это `tf.Tensor`, и элемент `numpy`этого тензора показывает значение признака:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "x2LT2JCqhoD_"
      },
      "outputs": [],
      "source": [
        "for parsed_record in parsed_dataset.take(10):\n",
        "  print(repr(parsed_record))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Cig9EodTlDmg"
      },
      "source": [
        "Здесь функция `tf.parse_example` распаковывает поля `tf.Example` в стандартные тензоры."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jyg1g3gU7DNn"
      },
      "source": [
        "## TFRecord файлы в Python"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3FXG3miA7Kf1"
      },
      "source": [
        "Модуль `tf.io` также содержит чисто Python функции для чтения и записи файлов TFRecord."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CKn5uql2lAaN"
      },
      "source": [
        "### Запись TFRecord файла"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LNW_FA-GQWXs"
      },
      "source": [
        "Далее запишем эти 10 000 наблюдений в файл `test.tfrecord`. Каждое наблюдения конвертируется в `tf.Example`-сообщение и затем пишется в файл. Вы можете после проверить, что файл `test.tfrecord` был создан:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "MKPHzoGv7q44"
      },
      "outputs": [],
      "source": [
        "# Запишем наблюдения `tf.Example` в файл.\n",
        "with tf.io.TFRecordWriter(filename) as writer:\n",
        "  for i in range(n_observations):\n",
        "    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])\n",
        "    writer.write(example)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "EjdFHHJMpUUo"
      },
      "outputs": [],
      "source": [
        "!du -sh {filename}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2osVRnYNni-E"
      },
      "source": [
        "### Чтение TFRecord файла\n",
        "\n",
        "Эти сериализованные тензоры могут быть легко распарсены с использование `tf.train.Example.ParseFromString`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "U3tnd3LerOtV"
      },
      "outputs": [],
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "nsEAACHcnm3f"
      },
      "outputs": [],
      "source": [
        "for raw_record in raw_dataset.take(1):\n",
        "  example = tf.train.Example()\n",
        "  example.ParseFromString(raw_record.numpy())\n",
        "  print(example)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S0tFDrwdoj3q"
      },
      "source": [
        "## Упражнение: Чтение и запись данных изображений"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rjN2LFxFpcR9"
      },
      "source": [
        "Это пример того как читать и писать данные изображений используя TFRecords. Цель этого показать как, от начала до конца, ввести данные (в этом случае изображение) и записать данные в TFRecord файл, затем прочитать файл и показать изображение.\n",
        "\n",
        "Это будет полезно если, например, вы хотите использовать несколько моделей на одних и тех же входных данных. Вместо хранения сырых данных изображений, они могут быть предобработанны в формат TFRecords, и затем могут быть использованы во всех дальнейших обработках и моделированиях.\n",
        "\n",
        "Сперва давайте скачаем [это изображение](https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg) кота и покажем [это фото](https://upload.wikimedia.org/wikipedia/commons/f/fe/New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg) строительства моста Williamsburg, NYC."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5Lk2qrKvN0yu"
      },
      "source": [
        "### Получите изображения"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "3a0fmwg8lHdF"
      },
      "outputs": [],
      "source": [
        "cat_in_snow  = tf.keras.utils.get_file('320px-Felis_catus-cat_on_snow.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/320px-Felis_catus-cat_on_snow.jpg')\n",
        "williamsburg_bridge = tf.keras.utils.get_file('194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "7aJJh7vENeE4"
      },
      "outputs": [],
      "source": [
        "display.display(display.Image(filename=cat_in_snow))\n",
        "display.display(display.HTML('Image cc-by: \u003ca \"href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg\"\u003eVon.grzanka\u003c/a\u003e'))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "KkW0uuhcXZqA"
      },
      "outputs": [],
      "source": [
        "display.display(display.Image(filename=williamsburg_bridge))\n",
        "display.display(display.HTML('\u003ca \"href=https://commons.wikimedia.org/wiki/File:New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg\"\u003eFrom Wikimedia\u003c/a\u003e'))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VSOgJSwoN5TQ"
      },
      "source": [
        "### Write the TFRecord file"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Azx83ryQEU6T"
      },
      "source": [
        "Как и ранее закодируйте признаки как типы совместимые с `tf.Example`. Здесь хранится необработанные данные изображения в формате string, так же как и высота, ширина, глубина и произвольный признак `label`. Последнее используется когда вы пишете файл чтобы различать изображение кота и моста. Используйте `0` изображения кота, и `1` для моста:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "kC4TS1ZEONHr"
      },
      "outputs": [],
      "source": [
        "image_labels = {\n",
        "    cat_in_snow : 0,\n",
        "    williamsburg_bridge : 1,\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "c5njMSYNEhNZ"
      },
      "outputs": [],
      "source": [
        "# Это пример использования только изображения кота.\n",
        "image_string = open(cat_in_snow, 'rb').read()\n",
        "\n",
        "label = image_labels[cat_in_snow]\n",
        "\n",
        "# Создайте библиотеку с признаками которые могут быть релевантны.\n",
        "def image_example(image_string, label):\n",
        "  image_shape = tf.image.decode_jpeg(image_string).shape\n",
        "\n",
        "  feature = {\n",
        "      'height': _int64_feature(image_shape[0]),\n",
        "      'width': _int64_feature(image_shape[1]),\n",
        "      'depth': _int64_feature(image_shape[2]),\n",
        "      'label': _int64_feature(label),\n",
        "      'image_raw': _bytes_feature(image_string),\n",
        "  }\n",
        "\n",
        "  return tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "\n",
        "for line in str(image_example(image_string, label)).split('\\n')[:15]:\n",
        "  print(line)\n",
        "print('...')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2G_o3O9MN0Qx"
      },
      "source": [
        "Заметьте что все признаки сейчас содержатся в `tf.Example`-сообщении. Далее функционализируйте вышеприведенный код и запишите пример сообщений в файл с именем `images.tfrecords`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "qcw06lQCOCZU"
      },
      "outputs": [],
      "source": [
        "# Запишем файлы изображений в `images.tfrecords`.\n",
        "# Сперва, преобразуем два изображения в `tf.Example`-сообщения.\n",
        "# Затем запишем их в `.tfrecords` файл.\n",
        "record_file = 'images.tfrecords'\n",
        "with tf.io.TFRecordWriter(record_file) as writer:\n",
        "  for filename, label in image_labels.items():\n",
        "    image_string = open(filename, 'rb').read()\n",
        "    tf_example = image_example(image_string, label)\n",
        "    writer.write(tf_example.SerializeToString())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "yJrTe6tHPCfs"
      },
      "outputs": [],
      "source": [
        "!du -sh {record_file}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jJSsCkZLPH6K"
      },
      "source": [
        "### Чтение TFRecord файла\n",
        "\n",
        "У вас сейчас есть файл `images.tfrecords` и вы можете проитерировать записи в нем чтобы прочитать то что вы в него записали. Поскольку этот пример содержит только изображение единственное свойство которое вам нужно это необработанная строка изображения. Извлеките ее используя геттеры описанные выше, а именно `example.features.feature['image_raw'].bytes_list.value[0]`. Вы можете также использовать метки чтобы определить, которая запись является котом, и которая мостом:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "M6Cnfd3cTKHN"
      },
      "outputs": [],
      "source": [
        "raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')\n",
        "\n",
        "# Создадим словарь описывающий свойства.\n",
        "image_feature_description = {\n",
        "    'height': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'width': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'depth': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'label': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'image_raw': tf.io.FixedLenFeature([], tf.string),\n",
        "}\n",
        "\n",
        "def _parse_image_function(example_proto):\n",
        "  # Распарсим входной tf.Example proto используя вышесозданный словарь.\n",
        "  return tf.io.parse_single_example(example_proto, image_feature_description)\n",
        "\n",
        "parsed_image_dataset = raw_image_dataset.map(_parse_image_function)\n",
        "parsed_image_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "0PEEFPk4NEg1"
      },
      "source": [
        "Восстановим изображение из TFRecord файла:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "yZf8jOyEIjSF"
      },
      "outputs": [],
      "source": [
        "for image_features in parsed_image_dataset:\n",
        "  image_raw = image_features['image_raw'].numpy()\n",
        "  display.display(display.Image(data=image_raw))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "pL--_KGdYoBz"
      ],
      "name": "tfrecord.ipynb",
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true,
      "version": "0.3.2"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
